﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Threading.Tasks;
using Dapper;
using Microsoft.Extensions.Logging;
using DapperExtensions;

namespace Micro.Extensions.Data
{
    public class DbContext : IDbContext
    {
        private readonly IDbConnectionFactory _dbConnectionFactory;
        private readonly ILogger<DbContext> _logger;
        private readonly List<Func<IDbConnection, IDbTransaction, Task<int>>> _commands;
        public DbContext(IDbConnectionFactory dbConnectionFactory, ILogger<DbContext> logger)
        {
            _dbConnectionFactory = dbConnectionFactory;
            _logger = logger;
            _commands = new List<Func<IDbConnection, IDbTransaction, Task<int>>>();
        }


        private IDbConnection GetDbConnection(bool readMaster = false)
        {
            return _dbConnectionFactory.GetDbConnection(readMaster);
        }
        #region Execute

        public void AddCommand(Func<IDbConnection, IDbTransaction, Task<int>> func)
        {
            _commands.Add(func);
        }

        public async Task<int> CommitAsync()
        {
            try
            {
                var count = 0;
                using var conn = _dbConnectionFactory.GetDbConnection();
                if (conn.State != ConnectionState.Open)
                {
                    await conn.OpenAsync();
                }

                using var tran =  conn.BeginTransaction();
                foreach (var func in _commands)
                {
                    count += await func(conn, tran);
                }

                tran.Commit();
                return count;
            }
            catch (Exception e)
            {
                _logger.LogError(e, "事务提交失败");
                throw;
            }
            finally
            {
                _commands.Clear();
            }


        }
        #endregion

        #region Query

        public T Get<T>(object id, IDbTransaction transaction = null, int? commandTimeout = null, bool readMaster = false) where T : class
        {
            using var conn = GetDbConnection(readMaster);
            return conn.Get<T>(id, transaction, commandTimeout);
        }
        public async Task<IEnumerable<T>> QueryAsync<T>(string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null, bool readMaster=false)
        {
            using var conn = GetDbConnection(readMaster);
            return await conn.QueryAsync<T>(sql, param, transaction, commandTimeout);
        }

        public async Task<T> QueryFirstAsync<T>(string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null, bool readMaster = false)
        {
            using var conn = GetDbConnection(readMaster);
            return await conn.QueryFirstAsync<T>(sql, param, transaction, commandTimeout);
        }

        public async Task<T> QueryFirstOrDefaultAsync<T>(string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null, bool readMaster = false)
        {
            using var conn = GetDbConnection(readMaster);
            return await conn.QueryFirstOrDefaultAsync<T>(sql, param, transaction, commandTimeout);
        }

        public async Task<T> QuerySingleAsync<T>(string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null, bool readMaster = false)
        {
            using var conn = GetDbConnection(readMaster);
            return await conn.QuerySingleAsync<T>(sql, param, transaction, commandTimeout);
        }
        public async Task<T> QuerySingleOrDefaultAsync<T>(string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null, bool readMaster = false)
        {
            using var conn = GetDbConnection(readMaster);
            return await conn.QuerySingleOrDefaultAsync<T>(sql, param, transaction, commandTimeout);
        }

        public async Task<SqlMapper.GridReader> QueryMultipleAsync(string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null, bool readMaster = false)
        {
            using var conn = GetDbConnection(readMaster);
            return await conn.QueryMultipleAsync(sql, param, transaction, commandTimeout);
        }
        #endregion



    }
}
